﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace Mindless
{
    public partial class Program
    {
        static object Choose(string prompt, IList choices)
        {
            WriteLine();
            WriteLine("{0}", prompt);

            int iChoice = 0;
            foreach (object obj in choices)
                WriteLine("   {0}: {1}", iChoice++, obj);

            bool isGood = false;
            do
            {
                WriteLine();
                Write("> ");

                string sChoice = ReadLine();

                if (choices.Count == 0)
                    return null;

                try
                {
                    iChoice = int.Parse(sChoice);
                    if (iChoice >= choices.Count)
                        throw new Exception("Try again");
                    isGood = true;
                }
                catch (Exception ex)
                {
                    WriteLine(ex.Message);
                }

            } while (!isGood);

            return choices[iChoice];
        }

        static string[] seats = { "S", "W", "N", "E" };
        static string[] bids = { "P", null, "2", "3", "4" };

        public static void Display(Deal deal)
        {
            WriteLine();
            for (int iTrick = 0; iTrick <= deal.ActiveTrick; ++iTrick)
            {
                if (iTrick == 0)
                    Write("Auction: ");
                else
                    Write("Trick {0}: ", iTrick);

                int stopPlayer = (iTrick == deal.ActiveTrick) ? deal.ActivePlayer : Settings.PlayerCount;
                for (int iPlayer = 0; iPlayer < stopPlayer; ++iPlayer)
                {
                    int iSeat = deal.GetSeat(iPlayer, iTrick);
                    if (iTrick == 0)
                    {
                        Bid bid = deal.Bids[iPlayer];
                        Write("{0}:{1}  ", seats[iSeat], bids[(int) bid.Level]);
                    }
                    else
                    {
                        Card card = deal.Tricks[iTrick, iPlayer];
                        Write("{0}:{1} ", seats[iSeat], card);
                    }
                }
                WriteLine();
            }

            WriteLine();
            for (int iSeat = 0; iSeat < Settings.PlayerCount; ++iSeat)
                WriteLine("{0}: {1}", seats[iSeat], deal.Hands[iSeat]);
        }

        static void Display(Game game)
        {
            IDictionary<int /*iTeam*/, List<string>> map = new Dictionary<int, List<string>>(Settings.TeamCount);
            for (int iTeam = 0; iTeam < Settings.TeamCount; ++iTeam)
                map[iTeam] = new List<string>();
            for (int iSeat = 0; iSeat < Settings.PlayerCount; ++iSeat)
                map[iSeat % Settings.TeamCount].Add(seats[iSeat]);

            WriteLine();
            for (int iTeam = 0; iTeam < Settings.TeamCount; ++iTeam)
                WriteLine("{0}: {1}", string.Join("+", map[iTeam].ToArray()), game.Score[iTeam]);
        }

        static string Continue(string prompt)
        {
            string[] choices = { "Continue", "Why?", "Override" };

            return (string) Choose(prompt, choices);
        }

        static void Auction(Game game, Deal deal)
        {
            bool apply = true;
            bool manual = false;
            while (deal.InAuction)
            {
                Display(deal);

                Bid choice;
                if ((deal.ActiveSeat == 0) || manual)
                {
                    IList<Bid> choices = new List<Bid>();
                    foreach (Bid bid in deal.GetChoices())
                        choices.Add(bid);
                    choices.Insert(0, new Bid(BidEnum.Pass));
                    choice = (Bid) Choose("Bid", (IList) choices);
                    apply = true;
                    manual = false;
                }
                else
                {
                    choice = (Bid) deal.EvaluateBid(game);
                    string prompt = string.Format("{0} bids {1}", seats[deal.ActiveSeat], choice);
                    string sChoice = Continue(prompt);
                    apply = (sChoice == "Continue");
                    manual = (sChoice == "Override");
                }

                if (apply)
                    deal.ChooseBid(choice);
            }
        }

        static void Playout(Game game, Deal deal)
        {
            bool apply = true;
            bool manual = false;
            while (!deal.IsOver)
            {
                Display(deal);

                Card choice;
                if ((deal.ActiveSeat == 0) || manual)
                {
                    IList<IChoice> choices = new List<IChoice>(deal.GetChoices());
                    choice = (Card) Choose("Play a card", (IList) choices);
                    apply = true;
                    manual = false;
                }
                else
                {
                    choice = (Card) deal.Evaluate(game, !apply);
                    string prompt = string.Format("{0} plays {1}", seats[deal.ActiveSeat], choice);
                    string sChoice = Continue(prompt);
                    apply = (sChoice == "Continue");
                    manual = (sChoice == "Override");
                }

                if (apply)
                    deal.ChooseCard(choice);
            }

            int[] points;
            Deal.BidStateEnum bidState = deal.BidState(false, out points);
            if (bidState == Deal.BidStateEnum.Set)
                deal.ApplySetback(points);
            game.Push(points);
        }

        static void PlayInteractive()
        {
            Hand[] hands =
            {
                Hand.Parse("K♥ K43♣ Q5♦"),
                Hand.Parse("KJ♠ J75♥ T♦"),
                Hand.Parse("Q♠ Q♥ J♣ 832♦"),
                Hand.Parse("754♠ 4♥ Q2♣")
            };
            Deal deal = new Deal(hands, 2);
            deal.ChooseBid(new Bid(BidEnum.Pass));
            deal.ChooseBid(new Bid(BidEnum.Pass));
            deal.ChooseBid(new Bid(BidEnum.Two));
            deal.ChooseBid(new Bid(BidEnum.Pass));
            deal.ChooseCard(Card.GetCard(Card.RankEnum.King, Card.SuitEnum.Clubs));
            deal.ChooseCard(Card.GetCard(Card.RankEnum.Five, Card.SuitEnum.Hearts));
            deal.ChooseCard(Card.GetCard(Card.RankEnum.Jack, Card.SuitEnum.Clubs));
            deal.ChooseCard(Card.GetCard(Card.RankEnum.Queen, Card.SuitEnum.Clubs));
            Display(deal);
            Game game = new Game();
            // game.Push(new int[] { 1, 10 });
            Card card = (Card) deal.Evaluate(game, true);
            WriteLine(card.ToString());
        }

        static void HandleException(Exception ex)
        {
            Program.WriteLine(ex.Message);
            Program.WriteLine(ex.StackTrace);
        }

        static void OnUnhandledException(object sender, UnhandledExceptionEventArgs args)
        {
            Exception ex = (Exception) args.ExceptionObject;
            HandleException(ex);
        }

        static void Main(string[] args)
        {
            try
            {
                AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException);
                // Application.ThreadException += new ThreadExceptionEventHandler(OnThreadException);
                if (new FileInfo(MasterFile).Exists)
                    PlayKSetback();
                else
                    PlayInteractive();
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        static Program()
        {
            log = new StreamWriter("mindless.log", false, Encoding.Unicode);
            log.AutoFlush = true;
        }
        static StreamWriter log;

        static public void WriteLine()
        {
            lock (log)
            {
                Console.WriteLine();
                log.WriteLine();
            }
        }

        static public void WriteLine(string format, params object[] args)
        {
            lock (log)
            {
                Console.WriteLine(format, args);
                log.WriteLine(format, args);
            }
        }

        static public void Write(string format, params object[] args)
        {
            lock (log)
            {
                Console.Write(format, args);
                log.Write(format, args);
            }
        }

        static public string ReadLine()
        {
            lock (log)
            {
                string result = Console.ReadLine();
                log.WriteLine(result);

                return result;
            }
        }
    }
}
